package com.stray.crawl.utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class ReflectUtils {
	
	public static List<Class<?>> getClasses(String packageName) throws IOException, ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        
        return classes;
	}
	
	public static <T> Object invoke(T obj, String method, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Class<?>[] clazzes = new Class<?>[args.length];
		
		for (int i = 0; i < args.length; i++) {
			clazzes[i] = args[i].getClass();
		}
		
		Class<?> clazz = obj.getClass();
		Method m = null;
		
		do {
			try {
				m = clazz.getDeclaredMethod(method, clazzes);
			} catch (Exception e) {
				// ignore error
			}
			if (m == null) {
				try {
					m = obj.getClass().getMethod(method, clazzes);
				} catch (Exception e) {
					// ignore error
				}
			}
			clazz = obj.getClass().getSuperclass();
		} while (clazz != null && m == null);
		
		if (m == null) {
			return null;
		}
	
		m.setAccessible(true);
		return m.invoke(obj, args);
	}
	
	public static <T> Object getField(T obj, String field) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException  {
		Field f = obj.getClass().getDeclaredField(field);
		
		if (f == null) {
			f = obj.getClass().getField(field);
		}
		
		if (f == null) {
			return null;
		}
		
		f.setAccessible(true);
		return f.get(obj);
	}
	
	public static void setField(Object obj, String field, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		Field f = obj.getClass().getDeclaredField(field);
		
		if (f == null) {
			f = obj.getClass().getField(field);
		}
		
		if (f == null) {
			return ;
		}
		
		f.setAccessible(true);
		f.set(obj, value);
	}
	
	private static List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException  {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        
        if (!directory.exists()) {
            return classes;
        }
        
        File[] files = directory.listFiles();
        for (File file : files) {
            String filename = file.getName();
            
			if (file.isDirectory()) {
                classes.addAll(findClasses(file, packageName + "." + filename));
            } else if (filename.endsWith(".class")) {
                classes.add(Class.forName(packageName + '.' + filename.substring(0, filename.length() - ".class".length())));
            }
        }
        
        return classes;
    }

}